package com.googlecode.YaccSyntaxTool.parser;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Scanner;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.googlecode.YaccSyntaxTool.grammar.Expression;
import com.googlecode.YaccSyntaxTool.grammar.Syntax;


/**
 * Parses the grammar section of an YACC logfile (y.output)
 *
 * @author goe
 *
 */
public class YOutputParser {

	/**
	 * Logger
	 */
	private final static Logger sLog = Logger.getLogger(YOutputParser.class.getName());

	private final static String		PROP_EXCLUDE		= "parser.exclude";

	// PARSER properties (=configuration)
	private Properties		props;

	private Set<String>		excludes; // <nonterminal>


	public YOutputParser() {
		super();
	}

	public Syntax parse(String filename) throws FileNotFoundException, ParseException {

		Scanner scanner = new Scanner(new FileReader(filename));

		String		nonterminal = "";	// current nonterminal
		Expression	expression = null;	// current expression

		loadProps();

		Syntax		syntax = new Syntax("Complete Syntax");

		try {
			while (scanner.hasNextLine()) {
				String line = scanner.nextLine();

				if (line.trim().isEmpty()) {
					// reached end of rule -> add it to the map
					syntax.addRule(nonterminal,expression,excludes);
					nonterminal = null;
					expression = null;
				//} else if (line.matches("^ *\\d+ [^ ]:")) {
				} else if (line.indexOf(": ")>-1) {
					// reached start of rule -> get nonterminal and create expression
					String[] splitByColon = line.split(": ");
					String[] splitBySpace = splitByColon[0].split(" ");
					nonterminal = splitBySpace[splitBySpace.length-1];
					expression = new Expression(splitByColon[1],excludes);
				} else if (line.indexOf("| ")>-1) {
					// found an additional term -> add it to the expression
					if (expression != null) {
						String[] splitBySpace = line.split("\\| ");
						expression.addTerm(splitBySpace[1],excludes);
					}
				} else {
					throw new ParseException("Could not identify line: "+line, 0);
				}
			}
			// add last rule to the map
			syntax.addRule(nonterminal,expression,excludes);

		} finally {
			scanner.close();
		}

		return syntax;
	}

	private void loadProps() {
		// load properties
		props = new Properties();
		try {
			BufferedInputStream stream = new BufferedInputStream(new FileInputStream("./res/parser.properties"));
			props.load(stream);
			stream.close();
		}
		catch (IOException e) {
			sLog.log(Level.SEVERE,"Error on reading file [parser.properties] in directory [./res]: "+e.getMessage(),e);
		}

		handleProps();
	}

	private void handleProps() {
		// prepare excludes
		excludes = new HashSet<String>();
		String excludeStr = props.getProperty(PROP_EXCLUDE);
		if (excludeStr != null) {
			excludes.addAll(Arrays.asList(excludeStr.split(",")));
		}
	}
}
